The SelectMany standard query operator flattens out any IEnumerable<T>
result elements, returning each element individually from those
enumerable sources before moving onto the next element in the result
sequence. In contrast, the Select extension method would stop at the first level and return the IEnumerable<T> element itself.
Listing 1 demonstrates how SelectMany differs from Select,
with each variation aiming to retrieve each individual word within a
set of phrase strings. To retrieve the words in Option 1, a sub for loop is required, but SelectMany
automatically performs this subiteration of the original result
collection, as shown in Option 2. Option 3 demonstrates that the same
result can be achieved using multiple from statements in a query expression (which maps the query to use SelectMany operator behind the scenes). The Console output is shown in Output 1.
Listing 1. Select versus SelectMany—SelectMany drills into an IEnumerable result, returning its elements—see Output 1
string[] sentence = new string[] { "The quick brown",
"fox jumps over", "the lazy dog."};
Console.WriteLine("option 1:"); Console.WriteLine("---------");
// option 1: Select returns three string[]'s with
// three strings in each.
IEnumerable<string[]> words1 =
sentence.Select(w => w.Split(' '));
// to get each word, we have to use two foreach loops
foreach (string[] segment in words1)
foreach (string word in segment)
Console.WriteLine(word);
Console.WriteLine();
Console.WriteLine("option 2:"); Console.WriteLine("---------");
// option 2: SelectMany returns nine strings
// (sub-iterates the Select result)
IEnumerable<string> words2 =
sentence.SelectMany(segment => segment.Split(' '));
// with SelectMany we have every string individually
foreach (var word in words2)
Console.WriteLine(word);
// option 3: identical to Opt 2 above written using
// the Query Expression syntax (multiple froms)
IEnumerable<string> words3 =
from segment in sentence
from word in segment.Split(' ')
select word;
|
Output 1.
option 1:
---------
The
quick
brown
fox
jumps
over
the
lazy
dog.
option 2:
---------
The
quick
brown
fox
jumps
over
the
lazy
dog.
|
How does the SelectMany extension method work? It creates a nested foreach loop over the original result, returning each subelement using yield return statements. A close facsimile of the code behind SelectMany takes the following form:
static IEnumerable<S> SelectManyIterator<T, S>(
this IEnumerable<T> source,
Func<T, IEnumerable<S>> selector)
{
foreach (T element in source)
{
foreach (S subElement in selector(element))
{
yield return subElement;
}
}
}